/*
	File:	video/model/Definition.hpp
	Date:	2013-06-01
	Author:	Asteroth
*/


#ifndef __ZEN_MODEL_DEFINITION_HPP__
#define __ZEN_MODEL_DEFINITION_HPP__

#include <set>
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/algorithm/string.hpp>
#include <JSL.hpp>

#include "Animation.hpp"
#include "Mesh.hpp"
#include "Bone.hpp"
#include "../Material.hpp"

namespace zen {

	namespace video {
		
		class TextureManager;
		class ModelManager;
		class ShaderManager;

		namespace model {

			class Definition {

			public:

				Definition();
				Definition(const Definition &other); // very heavy operation
				~Definition();
				
				Definition& operator = (const Definition &other); // very heavy operation

				std::string getName() const { return name; }
				glm::mat4 getView() const { return view; }

				glm::uint getMatrerialCount() const { return materialCount; }
				Material* getMatrerials() const { return materials; }

				glm::uint getBoneCount() const { return boneCount; }
				model::Bone** getBones() const { return bones; }

				glm::uint getMeshCount() const { return meshCount; }
				model::Mesh** getMeshes() const { return meshes; }

				glm::uint getAnimationCount() const { return animationCount; }
				model::Animation** getAnimations() const { return animations; }

				ModelManager* getModelManager() const { return modelManager; }
				void setModelManager(ModelManager* modelManager) { this->modelManager = modelManager; }

				TextureManager* getTextureManager() const { return textureManager; }
				void setTextureManager(TextureManager* textureManager) { this->textureManager = textureManager; }

				ShaderManager* getMaterialShaderManager() const { return materialShaderManager; }
				void setMaterialShaderManager(ShaderManager* materialShaderManager) { this->materialShaderManager = materialShaderManager; }

				Mesh* getMesh(glm::uint meshIndex) const { return meshIndex < meshCount ? meshes[meshIndex] : nullptr; }

				Bone* getBone(glm::uint boneIndex) const { return boneIndex < boneCount ? bones[boneIndex] : nullptr; }
				Bone* getBone(const std::string &boneName) const;

				Mesh* getMeshByBone(const std::string &boneName) const;
				Mesh* getMeshByBone(glm::uint boneIndex) const;
				
				void free() { free(true); }

				bool loadResource(const std::string &resource);
				bool loadResource(jsl::Node *node);


			private:
				
				std::string name;
				glm::mat4 view;

				glm::uint materialCount;
				Material* materials;

				glm::uint boneCount;
				model::Bone** bones;

				glm::uint meshCount;
				model::Mesh** meshes;

				glm::uint animationCount;
				model::Animation** animations;
				
				TextureManager* textureManager;
				ModelManager* modelManager;
				ShaderManager* materialShaderManager;

				void init();
				void free(bool reinitialize);

				/// Uses name and parentName to compute index and parentIndex, also sorts partially bones by level in hierarchy.
				bool createBoneHierarchy();


			};


			typedef ptr(Definition) PDefinition;


		} // namespace model

	} // namespace video

} // namespace zen

#endif // __ZEN_MODEL_DEFINITION_HPP__
